Customer Segmentation - Marcos Damián Pool Canul¶
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import f1_score as f1
from plotly.subplots import make_subplots
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import silhouette_score
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.svm import SVC
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import scikitplot as skplt
import numpy as np
import pandas as pd
import seaborn as sns
import plotly.offline as pyo
import plotly.express as ex
import plotly.graph_objs as go
import plotly.figure_factory as ff
import scipy.stats as stats
import warnings
warnings.filterwarnings(action="ignore")
pyo.init_notebook_mode()
sns.set_style('darkgrid')
plt.rc('figure', figsize=(18, 9))
1. Exploración de los datos¶
Analizar los datos y explorar los terminos¶
El conjunto de datos "BankChurners.csv" contiene información sobre clientes de un banco.
- ID_Customer: Identificador numérico único para cada cliente.
- Customer_Age: Edad del cliente.
- Gender: Género del cliente (M/F).
- Dependent_count: Número de dependientes.
- Income_Category: Categoría de ingresos del cliente.
- Months_on_book: Cantidad de meses que el cliente ha estado con el banco.
- Total_Relationship_Count: Número total de productos del banco que el cliente utiliza.
- Months_Inactive_12_mon: Número de meses en los que el cliente estuvo inactivo en los últimos 12 meses.
- Contacts_Count_12_mon: Número de veces que el cliente contactó al banco en los últimos 12 meses.
- Credit_Limit: Límite de crédito del cliente.
- Total_Revolving_Bal: Saldo rotativo total del cliente.
- Avg_Open_To_Buy: Promedio de crédito no utilizado por el cliente.
- Total_Trans_Amt: Monto total de las transacciones en el último año.
- Total_Trans_Ct: Cantidad total de transacciones en el último año.
- Avg_Utilization_Ratio: Ratio promedio de utilización del crédito.
# Cargando los datos del archivo CSV
file_path = '../files/BankChurners.csv'
df = pd.read_csv(file_path)
# Visualizando las primeras filas de los datos para comprender su estructura
df.head()
| ID_Customer | Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 768805383 | 45 | M | 3 | $60K - $80K | 39 | 5 | 1 | 3 | 12691.0 | 777 | 11914.0 | 1144 | 42 | 0.061 |
| 1 | 818770008 | 49 | F | 5 | Less than $40K | 44 | 6 | 1 | 2 | 8256.0 | 864 | 7392.0 | 1291 | 33 | 0.105 |
| 2 | 713982108 | 51 | M | 3 | $80K - $120K | 36 | 4 | 1 | 0 | 3418.0 | 0 | 3418.0 | 1887 | 20 | 0.000 |
| 3 | 769911858 | 40 | F | 4 | Less than $40K | 34 | 3 | 4 | 1 | 3313.0 | 2517 | 796.0 | 1171 | 20 | 0.760 |
| 4 | 709106358 | 40 | M | 3 | $60K - $80K | 21 | 5 | 1 | 0 | 4716.0 | 0 | 4716.0 | 816 | 28 | 0.000 |
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Customer_Age"],name='Histograma de edad'))
fig.update_xaxes(title_text="Edad del cliente")
fig.update_yaxes(title_text="Número de edad del cliente")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Customer_Age"], name='Boxplot de Edad'))
fig.update_xaxes(title_text="Edad del cliente")
fig.update_yaxes(title_text="Número de clientes")
fig.show()
ex.pie(df, names='Gender', title='Distribución de género',hole=0.33)
fig = ex.box(df, x='Gender', title='Distribución de género')
fig.show()
ex.pie(df, names='Dependent_count', title='Recuento de dependientes',hole=0.33)
ex.pie(df, names='Income_Category', title='Categoría de ingresos',hole=0.33)
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Months_on_book"],
name='Months on book', nbinsx=10))
fig.update_xaxes(title_text="Months on book (5 month periods)")
fig.update_yaxes(title_text="Number of Months on book")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Months_on_book"], name='Months on book'))
fig.update_xaxes(title_text="Months on book")
fig.update_yaxes(title_text="Number of Months on book")
fig.show()
ex.pie(df, names='Total_Relationship_Count', title='Recuento total de relaciones',hole=0.33)
fig = go.Figure()
fig.add_trace(go.Histogram(
x=df["Months_Inactive_12_mon"], name='Meses Inactivo 12 meses'))
fig.update_xaxes(title_text="Meses Inactivo 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Months_Inactive_12_mon"],
name='Meses Inactivo 12 meses'))
fig.update_xaxes(title_text="Meses Inactivo 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()
fig = go.Figure()
fig.add_trace(go.Histogram(
x=df["Contacts_Count_12_mon"], name='Contacts_Count_12_mon'))
fig.update_xaxes(title_text="Número de contactos 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Contacts_Count_12_mon"],
name='Contacts_Count_12_mon'))
fig.update_xaxes(title_text="Número de contactos 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Credit_Limit"],
name='Credit Limit', nbinsx=20))
fig.update_xaxes(title_text="Límite de crédito (períodos de 2K)")
fig.update_yaxes(title_text="Número de clientes que tienen límite de crédito")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Credit_Limit"], name='Credit Limit'))
fig.update_xaxes(title_text="Límite de crédito (períodos de 2K)")
fig.update_yaxes(title_text="Número de clientes que tienen límite de crédito")
fig.show()
fig = go.Figure()
fig.add_trace(go.Histogram(
x=df["Total_Revolving_Bal"], name='Saldo rotatorio total', nbinsx=20))
fig.update_xaxes(title_text="Saldo rotatorio total (2K períodos)")
fig.update_yaxes(title_text="Número de clientes Saldo rotatorio total")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Total_Revolving_Bal"],
name='Saldo rotatorio total'))
fig.update_xaxes(title_text="Saldo rotatorio total (2K períodos)")
fig.update_yaxes(title_text="Número de clientes Saldo rotatorio total")
fig.show()
fig = go.Figure()
fig.add_trace(go.Histogram(
x=df["Avg_Open_To_Buy"], name='Avg Open To Buy', nbinsx=20))
fig.update_xaxes(title_text="Avg Open To Buy (2K periods)")
fig.update_yaxes(title_text="Number of Customer Avg Open To Buy")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Avg_Open_To_Buy"], name='Avg Open To Buy'))
fig.update_xaxes(title_text="Avg Open To Buy (2K periods)")
fig.update_yaxes(title_text="Number of Customer Avg Open To Buy")
fig.show()
fig = go.Figure()
fig.add_trace(go.Histogram(
x=df["Total_Trans_Amt"], name='Total Trans Amt', nbinsx=20))
fig.update_xaxes(title_text="Total Trans Amt (1K periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Amt")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Total_Trans_Amt"], name='Total Trans Amt'))
fig.update_xaxes(title_text="Total Trans Amt (1K periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Amt")
fig.show()
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Total_Trans_Ct"],
name='Total_Trans_Ct', nbinsx=20))
fig.update_xaxes(title_text="Total Trans Ct (10 periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Ct")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Total_Trans_Ct"], name='Total_Trans_Ct'))
fig.update_xaxes(title_text="Total Trans Ct (10 periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Ct")
fig.show()
fig = go.Figure()
fig.add_trace(go.Histogram(
x=df["Avg_Utilization_Ratio"], name='Avg Utilization Ratio', nbinsx=10))
fig.update_xaxes(title_text="Avg Utilization Ratio (0.1 periods)")
fig.update_yaxes(title_text="Number of Customer Avg Utilization Ratio")
fig.show()
fig = go.Figure()
fig.add_trace(go.Box(x=df["Avg_Utilization_Ratio"],
name='Avg Utilization Ratio'))
fig.update_xaxes(title_text="Avg Utilization Ratio (0.1 periods)")
fig.update_yaxes(title_text="Number of Customer Avg Utilization Ratio")
fig.show()
# Crear una figura
fig = go.Figure()
# Añadir un boxplot para cada columna numérica en df
for column in df.select_dtypes(include=['float', 'int']).columns:
fig.add_trace(go.Box(y=df[column], name=column))
# Actualizar el layout de la figura, incluyendo la rotación de 90 grados en las etiquetas del eje X
fig.update_layout(xaxis_tickangle=90)
# Mostrar la figura
fig.show()
Box = df.plot(kind='box', subplots=True, layout=(4, 5), sharex=False, sharey=False, figsize=(15,15))
histogram = df.hist(figsize=(15, 15), bins=25)
# Correction Matrix Plot
correlations = df.corr()
# plot correlation matrix
fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111)
cax = ax.matshow(correlations, vmin=-1, vmax=1)
fig.colorbar(cax)
plt.show()
plt.figure(figsize=(9, 7))
sns.heatmap(df.corr(), cmap='coolwarm')
plt.title('Correlation Matrix')
Text(0.5, 1.0, 'Correlation Matrix')
df.shape
(10127, 15)
df['ID_Customer'].nunique()
10127
df[df['ID_Customer'].isna()]
| ID_Customer | Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio |
|---|
df[df.duplicated(['ID_Customer'])]
| ID_Customer | Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio |
|---|
df.columns
Index(['ID_Customer', 'Customer_Age', 'Gender', 'Dependent_count',
'Income_Category', 'Months_on_book', 'Total_Relationship_Count',
'Months_Inactive_12_mon', 'Contacts_Count_12_mon', 'Credit_Limit',
'Total_Revolving_Bal', 'Avg_Open_To_Buy', 'Total_Trans_Amt',
'Total_Trans_Ct', 'Avg_Utilization_Ratio'],
dtype='object')
df.dtypes
ID_Customer int64 Customer_Age int64 Gender object Dependent_count int64 Income_Category object Months_on_book int64 Total_Relationship_Count int64 Months_Inactive_12_mon int64 Contacts_Count_12_mon int64 Credit_Limit float64 Total_Revolving_Bal int64 Avg_Open_To_Buy float64 Total_Trans_Amt int64 Total_Trans_Ct int64 Avg_Utilization_Ratio float64 dtype: object
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10127 entries, 0 to 10126 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 ID_Customer 10127 non-null int64 1 Customer_Age 10127 non-null int64 2 Gender 10127 non-null object 3 Dependent_count 10127 non-null int64 4 Income_Category 10127 non-null object 5 Months_on_book 10127 non-null int64 6 Total_Relationship_Count 10127 non-null int64 7 Months_Inactive_12_mon 10127 non-null int64 8 Contacts_Count_12_mon 10127 non-null int64 9 Credit_Limit 10127 non-null float64 10 Total_Revolving_Bal 10127 non-null int64 11 Avg_Open_To_Buy 10127 non-null float64 12 Total_Trans_Amt 10127 non-null int64 13 Total_Trans_Ct 10127 non-null int64 14 Avg_Utilization_Ratio 10127 non-null float64 dtypes: float64(3), int64(10), object(2) memory usage: 1.2+ MB
Visualizamos la información del dataframe y podemos observar tanto el nombre de las columnas, como el su numero total que es de 15 y de igual manera podemos observar que contamos con el numero de filas que es de 10126, otra observación que tenemos es el tipo de datos con el que contamos, tenemos(3 columnas que son de tipos flotante),(10 que son de tipo entero) y (2 que son de tipo categoricas).
df.isnull().sum()
ID_Customer 0 Customer_Age 0 Gender 0 Dependent_count 0 Income_Category 0 Months_on_book 0 Total_Relationship_Count 0 Months_Inactive_12_mon 0 Contacts_Count_12_mon 0 Credit_Limit 0 Total_Revolving_Bal 0 Avg_Open_To_Buy 0 Total_Trans_Amt 0 Total_Trans_Ct 0 Avg_Utilization_Ratio 0 dtype: int64
df.describe()
| ID_Customer | Customer_Age | Dependent_count | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 1.012700e+04 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 |
| mean | 7.391776e+08 | 46.325960 | 2.346203 | 35.928409 | 3.812580 | 2.341167 | 2.455317 | 8631.953698 | 1162.814061 | 7469.139637 | 4404.086304 | 64.858695 | 0.274894 |
| std | 3.690378e+07 | 8.016814 | 1.298908 | 7.986416 | 1.554408 | 1.010622 | 1.106225 | 9088.776650 | 814.987335 | 9090.685324 | 3397.129254 | 23.472570 | 0.275691 |
| min | 7.080821e+08 | 26.000000 | 0.000000 | 13.000000 | 1.000000 | 0.000000 | 0.000000 | 1438.300000 | 0.000000 | 3.000000 | 510.000000 | 10.000000 | 0.000000 |
| 25% | 7.130368e+08 | 41.000000 | 1.000000 | 31.000000 | 3.000000 | 2.000000 | 2.000000 | 2555.000000 | 359.000000 | 1324.500000 | 2155.500000 | 45.000000 | 0.023000 |
| 50% | 7.179264e+08 | 46.000000 | 2.000000 | 36.000000 | 4.000000 | 2.000000 | 2.000000 | 4549.000000 | 1276.000000 | 3474.000000 | 3899.000000 | 67.000000 | 0.176000 |
| 75% | 7.731435e+08 | 52.000000 | 3.000000 | 40.000000 | 5.000000 | 3.000000 | 3.000000 | 11067.500000 | 1784.000000 | 9859.000000 | 4741.000000 | 81.000000 | 0.503000 |
| max | 8.283431e+08 | 73.000000 | 5.000000 | 56.000000 | 6.000000 | 6.000000 | 6.000000 | 34516.000000 | 2517.000000 | 34516.000000 | 18484.000000 | 139.000000 | 0.999000 |
Como podemos observar contamos con valores muy altos desde nuestros datos minimos como los valores maximos, es una técnica recomendable seria escalar nuestros datos para que mantengas un rango similar entre sus valores(0-1).
df.describe().T
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| ID_Customer | 10127.0 | 7.391776e+08 | 3.690378e+07 | 708082083.0 | 7.130368e+08 | 7.179264e+08 | 7.731435e+08 | 8.283431e+08 |
| Customer_Age | 10127.0 | 4.632596e+01 | 8.016814e+00 | 26.0 | 4.100000e+01 | 4.600000e+01 | 5.200000e+01 | 7.300000e+01 |
| Dependent_count | 10127.0 | 2.346203e+00 | 1.298908e+00 | 0.0 | 1.000000e+00 | 2.000000e+00 | 3.000000e+00 | 5.000000e+00 |
| Months_on_book | 10127.0 | 3.592841e+01 | 7.986416e+00 | 13.0 | 3.100000e+01 | 3.600000e+01 | 4.000000e+01 | 5.600000e+01 |
| Total_Relationship_Count | 10127.0 | 3.812580e+00 | 1.554408e+00 | 1.0 | 3.000000e+00 | 4.000000e+00 | 5.000000e+00 | 6.000000e+00 |
| Months_Inactive_12_mon | 10127.0 | 2.341167e+00 | 1.010622e+00 | 0.0 | 2.000000e+00 | 2.000000e+00 | 3.000000e+00 | 6.000000e+00 |
| Contacts_Count_12_mon | 10127.0 | 2.455317e+00 | 1.106225e+00 | 0.0 | 2.000000e+00 | 2.000000e+00 | 3.000000e+00 | 6.000000e+00 |
| Credit_Limit | 10127.0 | 8.631954e+03 | 9.088777e+03 | 1438.3 | 2.555000e+03 | 4.549000e+03 | 1.106750e+04 | 3.451600e+04 |
| Total_Revolving_Bal | 10127.0 | 1.162814e+03 | 8.149873e+02 | 0.0 | 3.590000e+02 | 1.276000e+03 | 1.784000e+03 | 2.517000e+03 |
| Avg_Open_To_Buy | 10127.0 | 7.469140e+03 | 9.090685e+03 | 3.0 | 1.324500e+03 | 3.474000e+03 | 9.859000e+03 | 3.451600e+04 |
| Total_Trans_Amt | 10127.0 | 4.404086e+03 | 3.397129e+03 | 510.0 | 2.155500e+03 | 3.899000e+03 | 4.741000e+03 | 1.848400e+04 |
| Total_Trans_Ct | 10127.0 | 6.485869e+01 | 2.347257e+01 | 10.0 | 4.500000e+01 | 6.700000e+01 | 8.100000e+01 | 1.390000e+02 |
| Avg_Utilization_Ratio | 10127.0 | 2.748936e-01 | 2.756915e-01 | 0.0 | 2.300000e-02 | 1.760000e-01 | 5.030000e-01 | 9.990000e-01 |
df.Income_Category[df.Income_Category == "Unknown"] = None
¿Es necesario estandarizar los datos?¶
Respuesta: Sí, es recomendable estandarizar los datos en este dataframe antes de aplicar k-medias, especialmente para las columnas numéricas con diferentes escalas. Además, considera transformar las variables categóricas a un formato numérico adecuado.
Para el algoritmo de k-medias, es importante estandarizar los datos cuando las columnas tienen diferentes escalas o unidades. Por ejemplo, columnas como "Customer_Age", "Credit_Limit", "Total_Trans_Amt", tienen diferentes rangos y unidades, lo que podría sesgar los resultados del algoritmo k-medias si no se estandarizan.
Además, para las columnas categóricas como "Gender" e "Income_Category", podrías considerar aplicar técnicas de codificación (como codificación one-hot) para convertirlas en un formato numérico antes de aplicar k-medias, ya que este algoritmo no maneja directamente datos categóricos.
La estandarización de los datos es necesaria antes de aplicar el algoritmo K-Means, ya que este algoritmo es sensible a la magnitud de los datos. La estandarización asegura que cada característica contribuya equitativamente al resultado del análisis y mejora la convergencia del algoritmo.
2. Preparación de los datos¶
Mantenemos la columna "Income_Category"¶
Lectura del Archivo CSV: Se carga un archivo CSV llamado 'BankChurners.csv' desde una ubicación específica en un DataFrame de Pandas llamado
dt.Transformación de 'Income_Category':
- Se define una función
clean_and_convert_income_categorypara convertir los valores textuales de la columna 'Income_Category' en valores numéricos. Por ejemplo, 'Less than $40K' se convierte en 39000 y '$60K - $80K' en 60000. Los valores 'Unknown' se convierten en 0. - Esta función se aplica a la columna 'Income_Category' del DataFrame.
- Se define una función
Transformación de 'Gender':
- Se define una función
convert_genderpara convertir los valores textuales de género en valores numéricos, donde 'M' se convierte en 1 y 'F' en 0. - Esta función se aplica a la columna 'Gender' del DataFrame.
- Se define una función
Visualización de Cambios: Se imprime un resumen de las primeras cinco filas del DataFrame para observar las transformaciones aplicadas.
El objetivo de estas operaciones es preparar los datos para un análisis más sencillo, convirtiendo información textual en numérica y estandarizando los formatos de datos.
La transformación de la columna 'Income_Category' de texto a números en el conjunto de datos tiene varias razones:
Facilitar el Análisis Estadístico: Los modelos estadísticos y algoritmos de machine learning suelen requerir que los datos estén en formato numérico. Convertir categorías de ingresos a números permite que estos modelos procesen y analicen los datos más efectivamente.
Consistencia en la Escala: Al convertir las categorías de ingresos en valores numéricos, se logra una escala consistente para esa variable. Esto es especialmente útil para técnicas analíticas que dependen de la magnitud de los valores, como la regresión lineal o los algoritmos basados en distancia.
Reducción de la Ambigüedad: Las categorías de texto como 'Less than $40K' son menos precisas y pueden ser interpretadas de diversas maneras. Al convertirlas en un valor numérico específico, se reduce la ambigüedad y se estandariza la interpretación de estos datos.
Mejora en la Comparabilidad: Los valores numéricos son más fáciles de comparar y ordenar que los textos. Por ejemplo, es más sencillo comparar ingresos de 30000 y 40000 que comparar categorías como 'Less than $40K' y '$30K - $40K'.
Simplificación de la Visualización: Los datos numéricos se pueden visualizar más fácilmente en gráficos y tablas, lo cual es importante para el análisis exploratorio de datos y la presentación de resultados.
Agrupación y Segmentación: La conversión a valores numéricos facilita la agrupación de clientes en segmentos basados en su ingreso, lo cual puede ser útil para estrategias de marketing, análisis de comportamiento del cliente, entre otros.
# Leer el archivo CSV
file_path = '../files/BankChurners.csv'
dt = pd.read_csv(file_path)
# Definir la función clean_income_category
def clean_and_convert_income_category(value):
if 'Less than' in value:
# Extraer el número, restar 1 y convertir a forma numérica completa
number = int(value.split('$')[1].split('K')[0]) - 1
return number * 1000
elif 'Unknown' in value:
return 0
else:
# Tomar el primer valor del rango y convertir a forma numérica completa
number = int(value.split(' ')[0].replace('$', '').replace('K', ''))
return number * 1000
# Definir la función convert_gender
def convert_gender(value):
if value == 'M':
return 1
elif value == 'F':
return 0
else:
return value # en caso de que haya algún valor inesperado
# Aplicar la función al dtFrame
dt['Income_Category'] = dt['Income_Category'].apply(
clean_and_convert_income_category)
# Aplicando la función convert_gender a la columna 'Gender'
dt['Gender'] = dt['Gender'].apply(convert_gender)
# Opcional: Mostrar los datos modificados
print(dt.head())
ID_Customer Customer_Age Gender Dependent_count Income_Category \ 0 768805383 45 1 3 60000 1 818770008 49 0 5 39000 2 713982108 51 1 3 80000 3 769911858 40 0 4 39000 4 709106358 40 1 3 60000 Months_on_book Total_Relationship_Count Months_Inactive_12_mon \ 0 39 5 1 1 44 6 1 2 36 4 1 3 34 3 4 4 21 5 1 Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy \ 0 3 12691.0 777 11914.0 1 2 8256.0 864 7392.0 2 0 3418.0 0 3418.0 3 1 3313.0 2517 796.0 4 0 4716.0 0 4716.0 Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio 0 1144 42 0.061 1 1291 33 0.105 2 1887 20 0.000 3 1171 20 0.760 4 816 28 0.000
dt.head(10)
| ID_Customer | Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 768805383 | 45 | 1 | 3 | 60000 | 39 | 5 | 1 | 3 | 12691.0 | 777 | 11914.0 | 1144 | 42 | 0.061 |
| 1 | 818770008 | 49 | 0 | 5 | 39000 | 44 | 6 | 1 | 2 | 8256.0 | 864 | 7392.0 | 1291 | 33 | 0.105 |
| 2 | 713982108 | 51 | 1 | 3 | 80000 | 36 | 4 | 1 | 0 | 3418.0 | 0 | 3418.0 | 1887 | 20 | 0.000 |
| 3 | 769911858 | 40 | 0 | 4 | 39000 | 34 | 3 | 4 | 1 | 3313.0 | 2517 | 796.0 | 1171 | 20 | 0.760 |
| 4 | 709106358 | 40 | 1 | 3 | 60000 | 21 | 5 | 1 | 0 | 4716.0 | 0 | 4716.0 | 816 | 28 | 0.000 |
| 5 | 713061558 | 44 | 1 | 2 | 40000 | 36 | 3 | 1 | 2 | 4010.0 | 1247 | 2763.0 | 1088 | 24 | 0.311 |
| 6 | 810347208 | 51 | 1 | 4 | 120000 | 46 | 6 | 1 | 3 | 34516.0 | 2264 | 32252.0 | 1330 | 31 | 0.066 |
| 7 | 818906208 | 32 | 1 | 0 | 60000 | 27 | 2 | 2 | 2 | 29081.0 | 1396 | 27685.0 | 1538 | 36 | 0.048 |
| 8 | 710930508 | 37 | 1 | 3 | 60000 | 36 | 5 | 2 | 0 | 22352.0 | 2517 | 19835.0 | 1350 | 24 | 0.113 |
| 9 | 719661558 | 48 | 1 | 2 | 80000 | 36 | 6 | 3 | 3 | 11656.0 | 1677 | 9979.0 | 1441 | 32 | 0.144 |
dt.describe()
| ID_Customer | Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 1.012700e+04 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 | 10127.000000 |
| mean | 7.391776e+08 | 46.325960 | 0.470919 | 2.346203 | 49831.045719 | 35.928409 | 3.812580 | 2.341167 | 2.455317 | 8631.953698 | 1162.814061 | 7469.139637 | 4404.086304 | 64.858695 | 0.274894 |
| std | 3.690378e+07 | 8.016814 | 0.499178 | 1.298908 | 28927.760351 | 7.986416 | 1.554408 | 1.010622 | 1.106225 | 9088.776650 | 814.987335 | 9090.685324 | 3397.129254 | 23.472570 | 0.275691 |
| min | 7.080821e+08 | 26.000000 | 0.000000 | 0.000000 | 0.000000 | 13.000000 | 1.000000 | 0.000000 | 0.000000 | 1438.300000 | 0.000000 | 3.000000 | 510.000000 | 10.000000 | 0.000000 |
| 25% | 7.130368e+08 | 41.000000 | 0.000000 | 1.000000 | 39000.000000 | 31.000000 | 3.000000 | 2.000000 | 2.000000 | 2555.000000 | 359.000000 | 1324.500000 | 2155.500000 | 45.000000 | 0.023000 |
| 50% | 7.179264e+08 | 46.000000 | 0.000000 | 2.000000 | 40000.000000 | 36.000000 | 4.000000 | 2.000000 | 2.000000 | 4549.000000 | 1276.000000 | 3474.000000 | 3899.000000 | 67.000000 | 0.176000 |
| 75% | 7.731435e+08 | 52.000000 | 1.000000 | 3.000000 | 60000.000000 | 40.000000 | 5.000000 | 3.000000 | 3.000000 | 11067.500000 | 1784.000000 | 9859.000000 | 4741.000000 | 81.000000 | 0.503000 |
| max | 8.283431e+08 | 73.000000 | 1.000000 | 5.000000 | 120000.000000 | 56.000000 | 6.000000 | 6.000000 | 6.000000 | 34516.000000 | 2517.000000 | 34516.000000 | 18484.000000 | 139.000000 | 0.999000 |
Manteniendo solo valores numericos¶
lista_n = ["ID_Customer", "Customer_Age","Gender", "Dependent_count", "Income_Category", "Months_on_book",
"Total_Relationship_Count", "Months_Inactive_12_mon", "Contacts_Count_12_mon",
"Credit_Limit", "Total_Revolving_Bal", "Avg_Open_To_Buy", "Total_Trans_Amt", "Total_Trans_Ct",
"Avg_Utilization_Ratio"] # Selecionamos las columnas numericas
Creamos un lista en la cual almacenamos los nombres de las columnas que contienen valores numericos, omitiendo las categoricas que en este caso son ("Gender" y "Income_Category")
dt_n = dt[lista_n]
dt_n.head()
| ID_Customer | Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 768805383 | 45 | 1 | 3 | 60000 | 39 | 5 | 1 | 3 | 12691.0 | 777 | 11914.0 | 1144 | 42 | 0.061 |
| 1 | 818770008 | 49 | 0 | 5 | 39000 | 44 | 6 | 1 | 2 | 8256.0 | 864 | 7392.0 | 1291 | 33 | 0.105 |
| 2 | 713982108 | 51 | 1 | 3 | 80000 | 36 | 4 | 1 | 0 | 3418.0 | 0 | 3418.0 | 1887 | 20 | 0.000 |
| 3 | 769911858 | 40 | 0 | 4 | 39000 | 34 | 3 | 4 | 1 | 3313.0 | 2517 | 796.0 | 1171 | 20 | 0.760 |
| 4 | 709106358 | 40 | 1 | 3 | 60000 | 21 | 5 | 1 | 0 | 4716.0 | 0 | 4716.0 | 816 | 28 | 0.000 |
Poniendo de indice la columna "ID_Customer"¶
# Seleccionamos la columnas que será el indice
df = dt_n.set_index("ID_Customer")
df.head()
| Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ID_Customer | ||||||||||||||
| 768805383 | 45 | 1 | 3 | 60000 | 39 | 5 | 1 | 3 | 12691.0 | 777 | 11914.0 | 1144 | 42 | 0.061 |
| 818770008 | 49 | 0 | 5 | 39000 | 44 | 6 | 1 | 2 | 8256.0 | 864 | 7392.0 | 1291 | 33 | 0.105 |
| 713982108 | 51 | 1 | 3 | 80000 | 36 | 4 | 1 | 0 | 3418.0 | 0 | 3418.0 | 1887 | 20 | 0.000 |
| 769911858 | 40 | 0 | 4 | 39000 | 34 | 3 | 4 | 1 | 3313.0 | 2517 | 796.0 | 1171 | 20 | 0.760 |
| 709106358 | 40 | 1 | 3 | 60000 | 21 | 5 | 1 | 0 | 4716.0 | 0 | 4716.0 | 816 | 28 | 0.000 |
Lo siguien que se realizo fue usar la el método "set_index" de pandas para sustutuir en indice anterios por la columna "ID_Customer", hacemos una visualización y verificamos que el indice se haya cambiado de manera correcta.
Tomando en cuenta la exploración de los graficos de caja, podemos observar que la mayoria de las columnas cuentan con valores atípicos.
plt.figure(figsize=(15, 6))
df.boxplot(rot=(90))
plt.show()
Al hacer un visualización a los datos podemos observar que con tamos con datos atipicos.
df.isnull().sum().sum()
0
Podemos observa que en el dataframe original no contamos con datos nulos solo atipicos.
Limpiando nuestros datos tomando en cuenta la Desviación estandar de 3 y 5.¶
Desviacion estandar de 3¶
media = df.mean() # sacamos la media para cada columna del dataframe
desviacion = df.std() # sacamos la desviación estandar de cada columna del dataframe
determinamos la media de cada columna y la desviación estandar
std_3 = 3 * desviacion # tomamos en cuenta la desviación estandar de 3
# hacemos el calculo para tomar solo la desviación estandar que estan por debajo
df_std_3 = df[(df-media).abs() <= std_3]
df_std_3.head()
| Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ID_Customer | ||||||||||||||
| 768805383 | 45.0 | 1 | 3 | 60000 | 39 | 5 | 1.0 | 3.0 | 12691.0 | 777 | 11914.0 | 1144.0 | 42.0 | 0.061 |
| 818770008 | 49.0 | 0 | 5 | 39000 | 44 | 6 | 1.0 | 2.0 | 8256.0 | 864 | 7392.0 | 1291.0 | 33.0 | 0.105 |
| 713982108 | 51.0 | 1 | 3 | 80000 | 36 | 4 | 1.0 | 0.0 | 3418.0 | 0 | 3418.0 | 1887.0 | 20.0 | 0.000 |
| 769911858 | 40.0 | 0 | 4 | 39000 | 34 | 3 | 4.0 | 1.0 | 3313.0 | 2517 | 796.0 | 1171.0 | 20.0 | 0.760 |
| 709106358 | 40.0 | 1 | 3 | 60000 | 21 | 5 | 1.0 | 0.0 | 4716.0 | 0 | 4716.0 | 816.0 | 28.0 | 0.000 |
Ya se ha aplicado la desviación estandar a 3
plt.figure(figsize=(15, 6)) # indicamos el tamaño que tendrá nuestro gráfico
df_std_3.boxplot(rot=90) # rotamos a 90°
plt.show() # visualizamos nuestra gráfica
df.dtypes
Customer_Age int64 Gender int64 Dependent_count int64 Income_Category int64 Months_on_book int64 Total_Relationship_Count int64 Months_Inactive_12_mon int64 Contacts_Count_12_mon int64 Credit_Limit float64 Total_Revolving_Bal int64 Avg_Open_To_Buy float64 Total_Trans_Amt int64 Total_Trans_Ct int64 Avg_Utilization_Ratio float64 dtype: object
Visualizamos el gráfico y podemos observar que los datos atipicos han disminuido significativamente.
df_std_3.isnull().sum().sum() # verificamos si contamos con datos nulos
572
Verificamos si al aplicar la desviación estandar a 3, y como podemos observar ahora contamos con datos nulos que son un total de 572, que es aproximadamente el 6% de los dato, dado a la cantidad de datos podemos obtar por eliminarlos.
Desviación estandar de 5¶
std_5 = 5 * desviacion
df_std_5 = df[(df-media).abs() <= std_5]
df_std_5.head()
| Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ID_Customer | ||||||||||||||
| 768805383 | 45 | 1 | 3 | 60000 | 39 | 5 | 1 | 3 | 12691.0 | 777 | 11914.0 | 1144 | 42 | 0.061 |
| 818770008 | 49 | 0 | 5 | 39000 | 44 | 6 | 1 | 2 | 8256.0 | 864 | 7392.0 | 1291 | 33 | 0.105 |
| 713982108 | 51 | 1 | 3 | 80000 | 36 | 4 | 1 | 0 | 3418.0 | 0 | 3418.0 | 1887 | 20 | 0.000 |
| 769911858 | 40 | 0 | 4 | 39000 | 34 | 3 | 4 | 1 | 3313.0 | 2517 | 796.0 | 1171 | 20 | 0.760 |
| 709106358 | 40 | 1 | 3 | 60000 | 21 | 5 | 1 | 0 | 4716.0 | 0 | 4716.0 | 816 | 28 | 0.000 |
plt.figure(figsize=(15, 6))
df_std_5.boxplot(rot=(90))
plt.show()
Al aplicar la desviación estandar a 5, podemos ver que los valores atipicos no han desminuido demasiado, por lo que se llega a la conclusión que es mejor trabajar con 3 desviaciones estandar ya que da mejores resultados al disminuir los datos atipicos.
df_std_5.isnull().sum().sum()
0
¿Hay datos nulos?¶
Si se identifican datos atípicos en el conjunto, pero no se detectan valores nulos.
Optamos por usar una desviación estandar de 3¶
Eliminando los datos nulos generados al utilizar 3 desviaciones estandar¶
df_std_3_1 = df_std_3.dropna() # eliminamos todos los datos nulos de nuestros dataframe
df_std_3_1.isnull().sum().sum() # verificamos si contamos con datos nulos
0
Eliminamos las filas que continen datos nulos y verificamos que la eliminación se haya realizado de manera correcta, como podemos observar ya no contamos con datos nulos.
#Identificar si datos atipicos
df_std_3_1.isnull().sum().sort_values()
Customer_Age 0 Gender 0 Dependent_count 0 Income_Category 0 Months_on_book 0 Total_Relationship_Count 0 Months_Inactive_12_mon 0 Contacts_Count_12_mon 0 Credit_Limit 0 Total_Revolving_Bal 0 Avg_Open_To_Buy 0 Total_Trans_Amt 0 Total_Trans_Ct 0 Avg_Utilization_Ratio 0 dtype: int64
Estandarizando (Escalando) nuestros datos.¶
Scaler = StandardScaler() # Creamos nuestro objeto escalable
Scaler.fit(df_std_3_1) # Aplicando la estandarización
dt_estandarizado = Scaler.transform(df_std_3_1) # Aplicamos las transformación a nuestro dataframe
Escalamos nuestros datos para que tengan magnitudes iguales las cuales se encuentren en un rago de (0 a 1).
3. Segmentación de datos¶
K-means: Técnica del Codo¶
# Inicializa una lista para almacenar los valores de la inercia (within-cluster sum of squares) para cada k
inertia_values = []
# Define el rango de valores de k que deseas probar
k_range = range(2, 11) # Prueba desde 2 hasta 10 clusters
# Itera sobre cada valor de k y ajusta el modelo K-Means
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42)
kmeans.fit(dt_estandarizado)
# Calcula la inercia y agrega el valor a la lista
inertia_values.append(kmeans.inertia_)
# Visualiza la técnica del codo para determinar el número óptimo de clusters
plt.plot(k_range, inertia_values, marker='o')
plt.xlabel('Número de clusters (k)')
plt.ylabel('Inercia')
plt.title('Técnica del codo')
plt.show()
¿Cuántos grupos son los ideales?¶
La justificación para usar 4 grupos en el algoritmo de k-means se basa en la observación de la técnica del codo aplicada durante la segmentación de datos. Según el análisis, la gráfica muestra una inclinación que se hacía menos pronunciada después de 4 clusters. Esto implica que añadir más clusters más allá de 4 no resultaría en una mejora significativa en la varianza explicada por los grupos.
En la técnica del codo, se busca el punto en el que aumentar el número de clusters deja de ofrecer un beneficio significativo en la reducción de la suma de las distancias al cuadrado de cada punto a su centro de cluster más cercano (inercia). Esto se puede visualizar como un cambio en la pendiente de la curva de inercia, donde se vuelve menos empinada, indicando que los clusters adicionales no están capturando tanta 'nueva varianza' en los datos como los clusters anteriores.
4. Perfilado de clientes¶
# Convertimos nuestros datos escalados en un dataframe
dt_estandarizado = pd.DataFrame(dt_estandarizado, columns=df_std_3_1.columns)
dt_estandarizado.head()
| Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -0.170050 | 1.073173 | 0.502005 | 0.356367 | 0.382027 | 0.737698 | -1.394283 | 0.517484 | 0.477434 | -0.466137 | 0.519532 | -1.076171 | -0.962610 | -0.785724 |
| 1 | 0.329850 | -0.931817 | 2.045394 | -0.370199 | 1.010997 | 1.385956 | -1.394283 | -0.408601 | -0.018040 | -0.359519 | 0.014723 | -1.019948 | -1.375910 | -0.627631 |
| 2 | 0.579801 | 1.073173 | 0.502005 | 1.048335 | 0.004645 | 0.089441 | -1.394283 | -2.260770 | -0.558537 | -1.418343 | -0.428910 | -0.791995 | -1.972900 | -1.004900 |
| 3 | -0.794925 | -0.931817 | 1.273700 | -0.370199 | -0.246943 | -0.558817 | 1.830299 | -1.334686 | -0.570268 | 1.666218 | -0.721615 | -1.065844 | -1.972900 | 1.725810 |
| 4 | -0.794925 | 1.073173 | 0.502005 | 0.356367 | -1.882266 | 0.737698 | -1.394283 | -2.260770 | -0.413526 | -1.418343 | -0.284009 | -1.201621 | -1.605522 | -1.004900 |
kmeans = KMeans(n_clusters=4, random_state=42)
dt_estandarizado['cluster'] = kmeans.fit_predict(dt_estandarizado)
# Realiza la perfilación de clientes para cada grupo
for grupo_num in range(4):
perfil_grupo = dt_estandarizado[dt_estandarizado['cluster']
== grupo_num].describe()
print(f"Perfil del cluster {grupo_num}:")
print(perfil_grupo)
print()
Perfil del cluster 0:
Customer_Age Gender Dependent_count Income_Category \
count 799.000000 799.000000 799.000000 799.000000
mean -0.083396 0.275192 0.082837 0.158520
std 0.904242 0.982027 0.998595 1.000452
min -2.419600 -0.931817 -1.813079 -1.719536
25% -0.669950 -0.931817 -0.269690 -0.370199
50% -0.170050 1.073173 -0.269690 -0.335601
75% 0.579801 1.073173 0.502005 1.048335
max 2.079501 1.073173 2.045394 2.432271
Months_on_book Total_Relationship_Count Months_Inactive_12_mon \
count 799.000000 799.000000 799.000000
mean -0.051089 -1.293076 -0.157992
std 0.949089 0.607122 0.951985
min -2.888619 -1.855332 -1.394283
25% -0.498531 -1.855332 -1.394283
50% 0.004645 -1.207075 -0.319423
75% 0.382027 -1.207075 0.755438
max 2.520526 1.385956 2.905160
Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal \
count 799.000000 799.000000 799.000000
mean -0.305445 0.619626 0.232077
std 0.842904 1.134447 0.920845
min -2.260770 -0.737064 -1.418343
25% -1.334686 -0.286669 -0.260867
50% -0.408601 0.265615 0.378227
75% 0.517484 1.311864 0.877001
max 2.369653 2.915704 1.666218
Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct \
count 799.000000 799.000000 799.000000
mean 0.598013 2.529214 1.549494
std 1.132895 1.136173 0.682604
min -0.748742 0.028022 -0.549309
25% -0.314374 1.528264 1.103893
50% 0.237098 1.904807 1.563116
75% 1.269657 3.764188 1.976416
max 3.042685 4.067679 3.170395
Avg_Utilization_Ratio cluster
count 799.000000 799.0
mean -0.408194 0.0
std 0.599348 0.0
min -1.004900 0.0
25% -0.834230 0.0
50% -0.598886 0.0
75% -0.153349 0.0
max 1.876717 0.0
Perfil del cluster 1:
Customer_Age Gender Dependent_count Income_Category \
count 1838.000000 1838.000000 1838.000000 1838.000000
mean 0.054266 1.009903 0.120356 1.151095
std 0.875305 0.350597 0.936159 0.986901
min -2.544575 -0.931817 -1.813079 -1.719536
25% -0.544975 1.073173 -0.269690 0.356367
50% 0.079900 1.073173 0.502005 1.048335
75% 0.704776 1.073173 0.502005 2.432271
max 2.329451 1.073173 2.045394 2.432271
Months_on_book Total_Relationship_Count Months_Inactive_12_mon \
count 1838.000000 1838.000000 1838.000000
mean 0.055428 0.167387 -0.001292
std 0.901276 0.918822 0.970950
min -2.888619 -1.855332 -2.469144
25% -0.498531 -0.558817 -0.319423
50% 0.004645 0.089441 -0.319423
75% 0.633615 0.737698 0.755438
max 2.520526 1.385956 2.905160
Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal \
count 1838.000000 1838.000000 1838.000000
mean 0.117423 1.446687 0.025396
std 1.033306 1.036132 0.969071
min -2.260770 -0.593617 -1.418343
25% -0.408601 0.545331 -0.667730
50% 0.517484 1.276170 0.149060
75% 0.517484 2.521978 0.725041
max 2.369653 2.915704 1.666218
Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct \
count 1838.000000 1838.000000 1838.000000
mean 1.443272 -0.324195 -0.327768
std 1.041659 0.653332 0.895163
min -0.588770 -1.311008 -2.432123
25% 0.552130 -0.864186 -1.100377
50% 1.289472 -0.412393 -0.227853
75% 2.531289 0.043226 0.415059
max 3.042685 2.429556 1.654960
Avg_Utilization_Ratio cluster
count 1838.000000 1838.0
mean -0.751375 1.0
std 0.229216 0.0
min -1.004900 1.0
25% -0.922260 1.0
50% -0.789317 1.0
75% -0.642003 1.0
max 0.292187 1.0
Perfil del cluster 2:
Customer_Age Gender Dependent_count Income_Category \
count 2858.000000 2858.000000 2858.000000 2858.000000
mean -0.032875 -0.302539 -0.027219 -0.415644
std 1.020902 0.930595 1.011842 0.749714
min -2.544575 -0.931817 -1.813079 -1.719536
25% -0.669950 -0.931817 -1.041384 -0.370199
50% -0.045075 -0.931817 -0.269690 -0.370199
75% 0.579801 1.073173 0.502005 -0.335601
max 2.704376 1.073173 2.045394 2.432271
Months_on_book Total_Relationship_Count Months_Inactive_12_mon \
count 2858.000000 2858.000000 2858.000000
mean -0.018287 0.047025 0.086753
std 1.004926 0.956257 1.001412
min -2.888619 -1.855332 -2.469144
25% -0.624325 -0.558817 -0.319423
50% 0.004645 0.089441 -0.319423
75% 0.507821 0.737698 0.755438
max 2.520526 1.385956 2.905160
Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal \
count 2858.000000 2858.000000 2858.000000
mean 0.116980 -0.339665 -0.996148
std 0.994613 0.476375 0.616368
min -2.260770 -0.779708 -1.418343
25% -0.408601 -0.723407 -1.418343
50% 0.517484 -0.478323 -1.418343
75% 0.517484 -0.112750 -0.501982
max 2.369653 2.165845 1.467688
Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct \
count 2858.000000 2858.000000 2858.000000
mean -0.248664 -0.249258 -0.193404
std 0.454378 0.562795 0.888322
min -0.722251 -1.318657 -2.432123
25% -0.604092 -0.697907 -0.962610
50% -0.392127 -0.245827 -0.044164
75% -0.044917 0.179385 0.552825
max 2.293397 2.422290 1.838649
Avg_Utilization_Ratio cluster
count 2858.000000 2858.0
mean -0.787522 2.0
std 0.337074 0.0
min -1.004900 2.0
25% -1.004900 2.0
50% -1.004900 2.0
75% -0.584514 2.0
max 0.615561 2.0
Perfil del cluster 3:
Customer_Age Gender Dependent_count Income_Category \
count 4065.000000 4065.000000 4065.000000 4065.000000
mean 0.014969 -0.298013 -0.051565 -0.259399
std 1.053144 0.932350 1.014681 0.726251
min -2.544575 -0.931817 -1.813079 -1.719536
25% -0.794925 -0.931817 -1.041384 -0.370199
50% -0.045075 -0.931817 -0.269690 -0.370199
75% 0.829751 1.073173 0.502005 -0.335601
max 2.954326 1.073173 2.045394 2.432271
Months_on_book Total_Relationship_Count Months_Inactive_12_mon \
count 4065.000000 4065.000000 4065.000000
mean -0.002163 0.145416 -0.029356
std 1.047009 0.944152 1.015982
min -2.888619 -1.855332 -2.469144
25% -0.624325 -0.558817 -0.319423
50% 0.004645 0.089441 -0.319423
75% 0.633615 0.737698 0.755438
max 2.520526 1.385956 2.905160
Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal \
count 4065.000000 4065.000000 4065.000000
mean -0.075302 -0.537104 0.643268
std 0.997447 0.251263 0.609653
min -2.260770 -0.779708 -0.790892
25% -0.408601 -0.689025 0.195628
50% -0.408601 -0.622664 0.618423
75% 0.517484 -0.472849 1.086560
max 2.369653 1.412970 1.666218
Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct \
count 4065.000000 4065.000000 4065.000000
mean -0.595293 -0.175299 -0.020384
std 0.233251 0.551045 0.892506
min -0.810140 -1.285765 -2.340278
25% -0.733225 -0.702497 -0.778921
50% -0.690915 -0.008695 0.185447
75% -0.539205 0.229202 0.690592
max 1.260113 2.423437 1.838649
Avg_Utilization_Ratio cluster
count 4065.000000 4065.0
mean 0.973656 3.0
std 0.711143 0.0
min -0.602479 3.0
25% 0.417944 3.0
50% 1.017981 3.0
75% 1.517413 3.0
max 2.584546 3.0
dt_estandarizado.head(10)
| Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | cluster | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -0.170050 | 1.073173 | 0.502005 | 0.356367 | 0.382027 | 0.737698 | -1.394283 | 0.517484 | 0.477434 | -0.466137 | 0.519532 | -1.076171 | -0.962610 | -0.785724 | 1 |
| 1 | 0.329850 | -0.931817 | 2.045394 | -0.370199 | 1.010997 | 1.385956 | -1.394283 | -0.408601 | -0.018040 | -0.359519 | 0.014723 | -1.019948 | -1.375910 | -0.627631 | 2 |
| 2 | 0.579801 | 1.073173 | 0.502005 | 1.048335 | 0.004645 | 0.089441 | -1.394283 | -2.260770 | -0.558537 | -1.418343 | -0.428910 | -0.791995 | -1.972900 | -1.004900 | 2 |
| 3 | -0.794925 | -0.931817 | 1.273700 | -0.370199 | -0.246943 | -0.558817 | 1.830299 | -1.334686 | -0.570268 | 1.666218 | -0.721615 | -1.065844 | -1.972900 | 1.725810 | 3 |
| 4 | -0.794925 | 1.073173 | 0.502005 | 0.356367 | -1.882266 | 0.737698 | -1.394283 | -2.260770 | -0.413526 | -1.418343 | -0.284009 | -1.201621 | -1.605522 | -1.004900 | 2 |
| 5 | -0.295025 | 1.073173 | -0.269690 | -0.335601 | 0.004645 | -0.558817 | -1.394283 | -0.408601 | -0.492399 | 0.109844 | -0.502031 | -1.097589 | -1.789211 | 0.112535 | 3 |
| 6 | 0.579801 | 1.073173 | 1.273700 | 2.432271 | 1.262586 | 1.385956 | -1.394283 | 0.517484 | 2.915704 | 1.356169 | 2.789945 | -1.005031 | -1.467755 | -0.767759 | 1 |
| 7 | -1.794725 | 1.073173 | -1.813079 | 0.356367 | -1.127502 | -1.207075 | -0.319423 | -0.408601 | 2.308510 | 0.292442 | 2.280113 | -0.925477 | -1.238143 | -0.832434 | 1 |
| 8 | -1.169850 | 1.073173 | 0.502005 | 0.356367 | 0.004645 | 0.737698 | -0.319423 | -2.260770 | 1.556752 | 1.666218 | 1.403786 | -0.997382 | -1.789211 | -0.598886 | 1 |
| 9 | 0.204875 | 1.073173 | -0.269690 | 1.048335 | 0.004645 | 1.385956 | 0.755438 | 0.517484 | 0.361805 | 0.636805 | 0.303521 | -0.962577 | -1.421833 | -0.487502 | 1 |
# dt_estandarizado.to_csv('dt_estandarizado.csv', index=False)
clusters_df = pd.read_csv('Clusters.csv')
cluster_profiles = clusters_df.groupby('cluster').mean()
cluster_profiles
| Customer_Age | Gender | Dependent_count | Income_Category | Months_on_book | Total_Relationship_Count | Months_Inactive_12_mon | Contacts_Count_12_mon | Credit_Limit | Total_Revolving_Bal | Avg_Open_To_Buy | Total_Trans_Amt | Total_Trans_Ct | Avg_Utilization_Ratio | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cluster | ||||||||||||||
| 0 | -0.083396 | 0.275192 | 0.082837 | 0.158520 | -0.051089 | -1.293076 | -0.157992 | -0.305445 | 0.619626 | 0.232077 | 0.598013 | 2.529214 | 1.549494 | -0.408194 |
| 1 | 0.054266 | 1.009903 | 0.120356 | 1.151095 | 0.055428 | 0.167387 | -0.001292 | 0.117423 | 1.446687 | 0.025396 | 1.443272 | -0.324195 | -0.327768 | -0.751375 |
| 2 | -0.032875 | -0.302539 | -0.027219 | -0.415644 | -0.018287 | 0.047025 | 0.086753 | 0.116980 | -0.339665 | -0.996148 | -0.248664 | -0.249258 | -0.193404 | -0.787522 |
| 3 | 0.014969 | -0.298013 | -0.051565 | -0.259399 | -0.002163 | 0.145416 | -0.029356 | -0.075302 | -0.537104 | 0.643268 | -0.595293 | -0.175299 | -0.020384 | 0.973656 |
Aplicamos la perfilación de los clientes que en este caso fue de 4 grupos, y como podemos observar en nuestro dataframe se ha agregado una nueva columna la cual nos indica el numero de grupos al que pertenece cada cliente.
Para realizar el perfilado de los 4 nuevos clústeres, examinaremos las estadísticas descriptivas proporcionadas para cada uno, enfocándonos en los valores medios para obtener una idea general de las características distintivas de los clientes en cada clúster.
Cluster 0: Clientes Activos y Cautelosos
- Edad promedio ligeramente por debajo de la media del conjunto de datos.
- Ligeramente más hombres que mujeres.
- Un número ligeramente superior de dependientes.
- Ingresos ligeramente por encima del promedio, indicando una posible tendencia hacia ingresos más altos.
- Duración promedio de la relación con el banco y un bajo número total de productos.
- Actividad reciente y contacto con el banco por debajo del promedio.
- Límites de crédito por encima del promedio y saldos de crédito rotativos ligeramente por encima del promedio.
- Alta utilización de la línea de crédito disponible.
- Transacciones totales significativamente por encima del promedio en cantidad y monto.
- Baja utilización del crédito en comparación con otros clústeres.
Este grupo está caracterizado por clientes relativamente jóvenes, con una distribución de género equilibrada y un número promedio de dependientes. Tienen ingresos moderados y una relación establecida con el banco, aunque utilizan menos productos financieros en comparación con otros clusters. Muestran una alta actividad de transacciones, lo que sugiere un uso activo de los servicios bancarios, y tienen un límite de crédito y saldo de crédito rotativo por encima del promedio. Sin embargo, la utilización del crédito es baja, lo que indica una gestión prudente de sus finanzas.
Cluster 1: Clientes Prósperos y Moderados
- Edad promedio ligeramente por encima del conjunto de datos.
- Predominantemente hombres.
- Número ligeramente superior de dependientes.
- Altos ingresos en comparación con otros clústeres.
- Duración de la relación con el banco y número total de productos ligeramente por encima del promedio.
- Nivel de inactividad y contacto con el banco promedio.
- Límites de crédito y saldos de crédito rotativos significativamente más altos.
- Una de las utilizaciones más bajas de la línea de crédito disponible.
- Actividad de transacciones por debajo del promedio tanto en cantidad como en monto.
Este grupo está compuesto principalmente por hombres de mediana edad con un número ligeramente superior de dependientes. Tienen ingresos significativamente altos y una relación estable con el banco, utilizando una cantidad moderada de productos financieros. Aunque muestran una actividad bancaria promedio, su utilización del crédito es muy baja, a pesar de tener un límite de crédito considerable y un saldo de crédito rotativo moderado. Las transacciones son un poco menos frecuentes y de menor valor en comparación con otros clusters.
Cluster 2: Clientes Conservadores y Moderados
- Edad promedio cercana a la media del conjunto de datos.
- Más mujeres que hombres.
- Dependientes y categoría de ingresos ligeramente por debajo del promedio.
- Duración promedio de la relación con el banco.
- Número total de productos y nivel de inactividad promedio.
- Contacto con el banco y límites de crédito por debajo del promedio.
- Saldos de crédito rotativos significativamente bajos.
- Utilización más baja del crédito disponible.
- Actividad de transacciones ligeramente por debajo del promedio en cantidad y monto.
En este grupo, encontramos clientes de diversas edades, pero en su mayoría mujeres, con ingresos relativamente bajos. Tienen una relación estable con el banco y utilizan una cantidad promedio de productos financieros. Su actividad bancaria es moderada, al igual que su límite de crédito y saldo de crédito rotativo, que son inferiores al promedio. La utilización del crédito es muy baja, lo que sugiere una gestión conservadora de sus recursos financieros.
Cluster 3: Clientes Dependientes y Utilizadores
- Edad promedio cercana a la media.
- Más mujeres que hombres.
- Dependientes y categoría de ingresos ligeramente por debajo del promedio.
- Duración promedio de la relación con el banco.
- Número total de productos y nivel de inactividad promedio.
- Menos contacto con el banco y límites de crédito por debajo del promedio.
- Saldos de crédito rotativos más altos que el promedio.
- La utilización más alta del crédito disponible en comparación con otros clústeres.
- Actividad de transacciones cercana al promedio tanto en cantidad como en monto.
Este grupo incluye clientes de diferentes edades, mayoritariamente mujeres, con ingresos moderados. Tienen una relación sólida con el banco y utilizan una cantidad ligeramente superior de productos financieros en comparación con otros clusters. Aunque muestran una actividad bancaria algo más baja que la media, tienen un límite de crédito y saldo de crédito rotativo relativamente bajos. Sin embargo, su alta utilización del crédito indica una dependencia significativa de los recursos bancarios para cubrir sus necesidades financieras.
5. Graficando los resultados¶
Aplicando PCA¶
Realizamos la reducción de dimenciones, con 2 componentes aplicando la tecnica de PCA, posteriormente lo convertimos en un dataframe para poder visualizarlo de una manera más clara.
# Aplicar PCA
pca = PCA(n_components=2) # Reducir a 2 componentes principales
data_pca = pca.fit_transform(dt_estandarizado)
# Aplicar K-Means con el número óptimo de clusters
kmeans = KMeans(n_clusters=4, random_state=0)
clusters = kmeans.fit_predict(data_pca)
# Crear un DataFrame para visualización
df_visual = pd.DataFrame(data_pca, columns=['PCA1', 'PCA2'])
df_visual['Cluster'] = clusters
Gráfica de dispersión¶
# Gráfico de dispersión de los clusters
plt.figure(figsize=(10, 10))
sns.scatterplot(x='PCA1', y='PCA2', hue='Cluster',
data=df_visual, palette='viridis')
plt.title('Clusters de Clientes en Componentes Principales')
plt.show()
En la siguiente Gráfica podemos observar los 2 componente que hemos reducido y podemos ver a los grupos que pertenecen, los grupos estan representados por diferentes colores.
6. Conclusiones¶
Este trabajo proporciona una visión profunda del comportamiento financiero de los clientes de un banco, utilizando técnicas de segmentación como el análisis de clústeres. A través del análisis de datos y la aplicación de algoritmos de aprendizaje no supervisado, se han identificado diferentes grupos de clientes con características financieras y comportamientos distintivos. Esto permite a la institución financiera comprender mejor a su base de clientes y adaptar estrategias específicas para satisfacer las necesidades de cada segmento.
Las conclusiones específicas de cada clúster proporcionan información valiosa sobre las preferencias, hábitos de consumo y perfiles de riesgo de los clientes, lo que puede guiar decisiones empresariales relacionadas con la oferta de productos, servicios y campañas de marketing personalizadas.
Este trabajo demuestra el valor de la segmentación de clientes en la industria bancaria para mejorar la satisfacción del cliente, la retención y lealtad, y, en última instancia, impulsar el crecimiento y la rentabilidad de la institución financiera.
Los pasos clave incluyeron la exploración y preparación de datos, la segmentación utilizando k-means y la interpretación de los resultados para perfilar distintos grupos de clientes.
Preparación y Exploración de Datos: Se realizó una cuidadosa preparación y exploración de los datos, lo que incluyó la estandarización de variables, tratamiento de valores atípicos y nulos, y la transformación de variables categóricas. Este paso fue crucial para garantizar la calidad y la utilidad de los datos para el análisis posterior.
Segmentación de Clientes: Utilizando el algoritmo k-means, se identificaron 4 clusters distintos dentro de la base de clientes. La técnica del codo se aplicó para determinar el número óptimo de clusters, asegurando una segmentación efectiva y significativa.
Perfilado de Clientes: Cada cluster reveló patrones únicos en términos de edad, ingresos, comportamiento de crédito y uso de productos bancarios. Estos perfiles proporcionan insights valiosos sobre las necesidades y preferencias de diferentes segmentos de clientes.
Esta práctica demuestra el poder de la minería de datos y el análisis de segmentación en la comprensión profunda de una base de clientes. Las estrategias basadas en datos pueden llevar a una mejor toma de decisiones y a un enfoque más centrado en el cliente en el sector bancario.
El análisis de segmentación de clientes en el ámbito bancario demostró ser una herramienta poderosa para entender mejor la base de clientes. A través de una meticulosa preparación y exploración de datos, seguida de la segmentación utilizando el algoritmo k-means, se identificaron perfiles de cliente distintivos. Estos perfiles proporcionan una base sólida para estrategias personalizadas de marketing y desarrollo de productos, y ofrecen insights valiosos para la toma de decisiones y la gestión de riesgos. La práctica destacó la importancia de un enfoque analítico y basado en datos en el sector financiero, subrayando el potencial de estos métodos para mejorar la comprensión y el servicio al cliente en un mercado competitivo.